<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
      }
      .header {
        height: 500px;
        width: 100%;
        background-color: green;
      }
      .playground {
        height: 2000px;
        width: 100%;
        background-color: aqua;
      }
      .animation-container {
        height: 100vh;
        width: 100%;
        background-color: red;
        position: sticky;
        top: 0;
        display: flex;
        align-items: center;
        justify-content: center;
      }
      .list {
        width: 680px;
        height: 200px;
        display: flex;
        flex-wrap: wrap;
        position: relative;
        box-sizing: border-box;
        text-align: center;
      }
      .list-item {
        width: 80px;
        height: 80px;
        background-color: green;
        margin-right: 20px;
        margin-bottom: 40px;
        border-radius: 5px;
      }
      .list-item:nth-child(7n) {
        margin-right: 0;
      }
      .list-item:nth-child(n + 7) {
        margin-bottom: 0;
      }
      .footer {
        height: 500px;
        width: 100%;
        background-color: blue;
      }
      .title {
        position: absolute;
        font-size: 22px;
        font-weight: 700;
        color: #fff;
      }
    </style>
  </head>
  <body>
    <div class="header"></div>
    <div class="playground">
      <div class="animation-container">
        <div class="title">上前一小步，文明一大步</div>
        <div class="list">
          <div data-order="0" class="list-item"></div>
          <div data-order="1" class="list-item"></div>
          <div data-order="3" class="list-item"></div>
          <div data-order="4" class="list-item"></div>
          <div data-order="3" class="list-item"></div>
          <div data-order="1" class="list-item"></div>
          <div data-order="0" class="list-item"></div>
          <div data-order="0" class="list-item"></div>
          <div data-order="1" class="list-item"></div>
          <div data-order="3" class="list-item"></div>
          <div data-order="4" class="list-item"></div>
          <div data-order="3" class="list-item"></div>
          <div data-order="1" class="list-item"></div>
          <div data-order="0" class="list-item"></div>
        </div>
      </div>
    </div>
    <div class="footer"></div>
  </body>

  <script>
    function createAnimate(scrollStart, scrollEnd, valueStart, valueEnd) {
      return function (x) {
        if (x < scrollStart) {
          return valueStart;
        }
        if (x > scrollEnd) {
          return valueEnd;
        }
        const bili = (valueEnd - valueStart) / (scrollEnd - scrollStart);
        return bili * (x - scrollStart) + valueStart;
      };
    }

    const animationMap = new Map();
    const items = document.querySelectorAll('.list-item');
    const playground = document.querySelector('.playground');
    const list = document.querySelector('.list');
    const title = document.querySelector('.title');

    const getDomAnimation = (scrollStart, scrollEnd, dom) => {
      scrollStart += dom.dataset.order * 200;
      const opacityAnimation = createAnimate(scrollStart, scrollEnd, 0, 1);
      const opacity = function (x) {
        return opacityAnimation(x);
      };
      const scaleAnimation = createAnimate(scrollStart, scrollEnd, 0.5, 1);
      const { clientWidth, clientHeight, offsetTop, offsetLeft } = dom;
      const rect = list.getBoundingClientRect();
      const xAnimation = createAnimate(
        scrollStart,
        scrollEnd,
        rect.width / 2 - clientWidth / 2 - offsetLeft,
        0
      );
      const yAnimation = createAnimate(
        scrollStart,
        scrollEnd,
        rect.height / 2 - clientHeight / 2 - offsetTop,
        0
      );
      const transform = function (x) {
        console.log(xAnimation(x));
        return `translate(${xAnimation(x)}px,${yAnimation(
          x
        )}px) scale(${scaleAnimation(x)}) `;
      };
      return {
        opacity,
        transform,
      };
    };
    const getTitleAnimation = (scrollStart, scrollEnd, dom) => {
      const opacityAnimation = createAnimate(scrollStart, scrollEnd, 1, 0);
      const yAnimation = createAnimate(scrollStart, scrollEnd, 0, -200);
      const transform = function (x) {
        return `translate(0,${yAnimation(x)}px)`;
      };
      const opacity = function (x) {
        return opacityAnimation(x);
      };
      return {
        transform,
        opacity,
      };
    };
    const updateMap = () => {
      const playgroundRect = playground.getBoundingClientRect();
      const scrollY = window.scrollY;
      const scrollStart = playgroundRect.top + scrollY;
      const scrollEnd = playgroundRect.bottom + scrollY - window.innerHeight;
      animationMap.set(
        title,
        getTitleAnimation(scrollStart, scrollStart + 500, title)
      );
      for (const item of items) {
        animationMap.set(
          item,
          getDomAnimation(scrollStart + 50, scrollEnd, item)
        );
      }
    };
    updateMap();
    const updateStyles = () => {
      const scrollY = window.scrollY;
      for (const [dom, animations] of animationMap) {
        for (const prop in animations) {
          const value = animations[prop](scrollY);
          dom.style[prop] = value;
        }
      }
    };
    updateStyles();
    window.addEventListener('scroll', updateStyles);
  </script>
</html>
